home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / progjour / 1991 / 06 / editor.c < prev    next >
C/C++ Source or Header  |  1991-10-02  |  31KB  |  934 lines

  1. /* edit.c-- Low-Level Workhorse Functions */
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <stdarg.h>
  6. #include <ctype.h>
  7. #include <dos.h>
  8. #include <conio.h>
  9. #include <string.h>
  10. #include <tools/viewport.h>
  11. #include <tools/textbuf.h>
  12.  
  13. #include <tools/window.h>
  14.  
  15. /* Interface functions to test the edit engine. They are described
  16.  * in engine.h
  17.  *
  18.  * These functions are used internally, they do not interface
  19.  * with the edit engine:
  20.  */
  21.  
  22. static int  draw    (win *const p, int ask_top, int ask_left );
  23. static int  hscroll (win *const p, int left);
  24. static int  bottom  (win *const p, int offset);
  25. static int  del_line(win *const p, textbuf *const b,int which_line);
  26. static int  export  (win *const p, textbuf *const b,int which_line);
  27. static void status  (win *const p, char *fmt, ... );
  28.  
  29. /*------------------------------------------------------------*/
  30. int     e_open( win *const this )
  31. {
  32.     /* Called when engine for a specific window is opened.
  33.      * Try to initialize buffer by importing the top few lines.
  34.      * On the first call, the import function should take care
  35.      * of both opening the file and adjusting this->bottom to hold
  36.      * the line number of the bottom line in the file. It should
  37.      * set up the line-number table at this time, as well.
  38.      */
  39.  
  40.     textbuf  *b = &this->b;
  41.     int nrows   = b_nrows(b);
  42.     int row, i;
  43.     char *new_line;
  44.  
  45.     if( this->import )
  46.     {
  47.         for( row = 0; row < nrows; ++row )
  48.         {
  49.             if( !(new_line = (*this->import)(this,row)) )
  50.                 break;
  51.  
  52.             bottom( this, +1 );
  53.             b_gotorc(b, row, 0);
  54.             for( i = b_ncols(b); --i >= 0 && *new_line ;)
  55.                 *b_advance(b) = *new_line++;
  56.         }
  57.         e_gotorc(this,0,0);
  58.         draw    (this,0,0);
  59.     }
  60.     return 1;
  61. }
  62. /*------------------------------------------------------------*/
  63. int     e_close( win *const this )
  64. {
  65.    /* Prepare for closing the window. Save the window state by
  66.     * exporting the current buffer and various important offsets.
  67.     * This function may be called several times.
  68.     */
  69.  
  70.    textbuf  *b = &this->b;
  71.    viewport *v = &this->v;
  72.    int nrows    = b_nrows(b);
  73.    int row;
  74.  
  75.    if( this->export )
  76.    {
  77.       for( row = 0; row < nrows && row <= this->bottom; ++row )
  78.       {
  79.           b_gotorc(b, row, 0);
  80.           if( !export( this, b, this->file_off + row ) )
  81.               break;
  82.       }
  83.       status( this, "%%%% row, viewport = %d %%%%",v_row(v)      );
  84.       status( this, "%%%% col, viewport = %d %%%%",v_col(v)      );
  85.       status( this, "%%%% row, offset   = %d %%%%",this->row_off );
  86.       status( this, "%%%% col, offset   = %d %%%%",this->col_off );
  87.       status( this, "%%%% file offset   = %d %%%%",this->file_off);
  88.    }
  89.    return 1;
  90. }
  91. /*------------------------------------------------------------
  92.  * Little stuff:
  93.  * e_vrow       Current viewport row (top == 0).
  94.  * e_vcol       Current viewport column (left == 0).
  95.  * e_vnrows     Number of rows in viewport.
  96.  * e_vncols     Number of columns in viewport.
  97.  * e_isword     True if c argument is a "word".
  98.  * e_maxrow     Largest virtual (file) row index
  99.  * e_maxcol     Largest virtual (file and buffer) column index
  100.  * e_col        Current virtual (file and buffer) cursor column
  101.  * e_row        Current virtual (file) cursor row
  102.  * e_cur        Character at current cursor position.
  103.  * e_last       Index of rightmost nonwhite character on line,
  104.  *              zero if the line is blank.
  105.  */
  106.  
  107. #pragma warn -par /* turn off warnings about arguments not used */
  108.  
  109. int  e_vrow   (win *const w) { return v_row     (&w->v);  }
  110. int  e_vcol   (win *const w) { return v_col     (&w->v);  }
  111. int  e_vnrows (win *const w) { return v_nrows   (&w->v);  }
  112. int  e_vncols (win *const w) { return v_ncols   (&w->v);  }
  113. int  e_isword (win *const w,
  114.                       int c) { return( isalnum(c) ||  c=='_' );}
  115. int  e_maxrow (win *const w) { return w->bottom;            }
  116. int  e_maxcol (win *const w) { return b_ncols(&w->b) -1;    }
  117. int  e_col    (win *const w) { return b_col(&w->b);         }
  118. int  e_row    (win *const w) { return b_row(&w->b)
  119.                                               + w->file_off;}
  120. int  e_cur    (win *const w) { return *b_current(&w->b);    }
  121. int  e_last   (win *const w) { return b_last(&w->b);        }
  122.  
  123. #pragma warn +par
  124. /*------------------------------------------------------------
  125.  * e_attention() and e_error();
  126.  *
  127.  * e_attention() rings the bell if not a horizontal motion. In
  128.  *               any event it sets the window's error flag.
  129.  * e_error()     returns the value of the flag and resets the
  130.  *               flag to zero.
  131.  */
  132.  
  133. void e_attention(win *const this, int h)
  134. {
  135.     if(!h)
  136.         putchar('\a');
  137.     this->error = 1;
  138. }
  139.  
  140. int     e_error( win *const this )
  141. {
  142.     int rv = this->error;
  143.     this->error = 0;
  144.     return rv;
  145. }
  146. /*------------------------------------------------------------*/
  147. int e_first     (win *const this)
  148. {
  149.     // Return the column index of the first nonwhite character
  150.     // on the current line or 0 if the line is blank
  151.  
  152.     textbuf *const b  = &this->b;
  153.     int     start_col = b_col(b);
  154.     int     start_row = b_row(b);
  155.     int     last_col  = b_ncols(b) - 1;
  156.     int     col;
  157.  
  158.     b_gotorc(b, start_row, 0);
  159.     for( col = 0; (col<last_col) && isspace(*b_current(b)); )
  160.     {
  161.         ++col;
  162.         b_advance(b);
  163.     }
  164.     if( isspace(*b_current(b)) ) /* line was entirely blank */
  165.         col = 0;
  166.     b_gotorc( b, start_row, start_col );
  167.     return col;
  168. }
  169. /*------------------------------------------------------------*/
  170. int e_find_c(win *const this, int c, int forward, int incl)
  171. {
  172.     /* If the character is found on the current line (to the
  173.      * right of the current cursor position if "forward" is true,
  174.      * to the left otherwise) return the column index of c.
  175.      * Otherwise return -1. If "incl" is false, return the index
  176.      * of the character next to the c rather than the index of
  177.      * c itself.
  178.      */
  179.  
  180.     textbuf *b    = &this->b;
  181.     int row       = b_row(b);
  182.     int col       = b_col(b);
  183.     int last      = b_last(b);
  184.     int start_col = col;
  185.     int rv        = -1;
  186.  
  187.     if( forward ) forward = 1;
  188.     else          forward = -1;
  189.  
  190.     col += forward;  /* start search at least one column    */
  191.     if( !incl )      /* away if not inclusive, add one more */
  192.         col += forward;
  193.  
  194.     if( !(0 <= col && col <= last) ) /* col isn't on the line */
  195.         rv = -1;
  196.     else
  197.     {
  198.         b_gotorc(b, row, col);
  199.         while( *b_current(b) != c   &&   !b_ateol(b) )
  200.         {
  201.             col += forward;
  202.             b_gotorc(b, row, col);
  203.         }
  204.         rv = (*b_current(b) == c) ? col : -1 ;
  205.  
  206.         if(rv != -1  &&  !incl)        /* if not including, */
  207.             if( (rv -= forward) < 0 )  /* back up a notch   */
  208.                 rv = -1;
  209.  
  210.         b_gotorc(b, row, start_col);
  211.     }
  212.     return rv;
  213. }
  214. /*------------------------------------------------------------*/
  215. static void status( win *const this, char *fmt, ... )
  216. {
  217.     /* works like fprintf, but outputs string using the
  218.      * current window's export function. String is exported as
  219.      * line -1.
  220.      */
  221.  
  222.     char scratch[W_LINEMAX];
  223.     va_list args;
  224.     int len;
  225.     if( this->export )
  226.     {
  227.         va_start(args, fmt);
  228.         len = vsprintf(scratch, fmt, args);
  229.         va_end(args);
  230.         (*this->export)( this, -1, scratch, len+1, len );
  231.     }
  232. }
  233. /*---------------------------------------------------------------*/
  234. static int export( win *const this,textbuf *const b,int which_line)
  235. {
  236.     /* Export current buffer-cursor line as line which_line,
  237.      * cursor does not move.
  238.      */
  239.  
  240.     int row  = b_row(b);
  241.     int col  = b_col(b);
  242.  
  243.     char scratch[W_LINEMAX];
  244.     char *p;
  245.     int  i;
  246.  
  247.     if( this->export )
  248.     {
  249.         b_gotorc(b,row,0);
  250.         for(p=scratch, i=b_ncols(b); --i >= 0; )
  251.             *p++ = *b_advance(b);
  252.  
  253.         b_gotorc(b,row,col);
  254.         (*this->export)( this, which_line, scratch, b_ncols(b),
  255.                                                     b_last (b)+1);
  256.         return 1;
  257.     }
  258.     return 0;
  259. }
  260. /*------------------------------------------------------------*/
  261. static int del_line( win *const this, textbuf *const b,
  262.                                                 int which_line )
  263. {
  264.     /* export current buffer-cursor line for delete, cursor     */
  265.     /* does not move.                                           */
  266.  
  267.     int row  = b_row(b);
  268.     int col  = b_col(b);
  269.  
  270.     char  scratch[W_LINEMAX];
  271.     char  *p;
  272.     int   i;
  273.  
  274.     if( this->remove )
  275.     {
  276.         b_gotorc(b,row,0);
  277.         for(p=scratch, i=b_ncols(b); --i >= 0; )
  278.             *p++ = *b_advance(b);
  279.  
  280.         b_gotorc(b,row,col);
  281.         (*this->remove)( this, which_line, scratch,
  282.                                 b_ncols(b), b_last(b)+1);
  283.         return 1;
  284.     }
  285.     return 0;
  286. }
  287. /*------------------------------------------------------------*/
  288. int e_vscroll( win *const this, int up, int import )
  289. {
  290.     /* Scroll text up one line if if up > 0, down otherwise. If
  291.      * import is true, the scroll won't happen unless a new top
  292.      * or bottom line can be imported from the overflow file. If
  293.      * it's false, the scroll always happens, no attempt is made
  294.      * to do an import, and the bottom (or top) line will be
  295.      * filled with blanks. Return true on success, false
  296.      * otherwise.
  297.      */
  298.  
  299.     viewport *const v = &this->v ;
  300.     textbuf  *const b = &this->b ;
  301.     int          rv   = 1;          /* return value         */
  302.     int          brow = b_row(b);   /* buffer row/column    */
  303.     int          bcol = b_col(b);
  304.     int          vrow = v_row(v);   /* viewport row/column  */
  305.     int          vcol = v_col(v);
  306.     int          i;
  307.     char         *new_line;
  308.  
  309.     if( up > 0 )        /* Move text up, viewport moves down */
  310.     {                   /* relative to buffer                */
  311.  
  312.         if( (this->row_off + v_nrows(v)) < b_nrows(b) )
  313.         {
  314.             /* Viewport isn't banged up against bottom of
  315.              * buffer. Slide viewport over text, but don't have
  316.              * to modify buffer. Position buffer cursor at left
  317.              * edge of line with which to fill the viewport's
  318.              * opened up bottom line: the buffer line corres-
  319.              * ponding to the one just below the viewport.
  320.              * Slide the viewport by incrementing row_off.
  321.              */
  322.             b_gotorc(b, this->row_off+v_nrows(v), this->col_off);
  323.             ++this->row_off;
  324.         }
  325.         else if( !import )              /* import is suppressed */
  326.         {
  327.             new_line = "";      /* pretend we got an empty line */
  328.             goto got_bot;
  329.         }
  330.         else if( !this->import )  /* try to import bottom line */
  331.         {
  332.             rv = 0;                      /* no import function */
  333.             goto exit;
  334.         }
  335.         else if( (i = this->file_off + b_nrows(b)) > this->bottom )
  336.         {
  337.             rv = 0;                     /* line doesn't exist */
  338.             goto exit;
  339.         }
  340.         else if( !(new_line = (*this->import)(this,i)) )
  341.         {
  342.             rv = 0;                     /* couldn't import it */
  343.             goto exit;
  344.         }
  345.         else /* got the line, add it to the buffer & viewport */
  346.         {
  347. got_bot:
  348.             b_gotorc(b, 0, 0);          /* export the top line */
  349.             export  (this, b, this->file_off );
  350.  
  351.             ++this->file_off ;
  352.             b_closedown (b, ' ');    /* delete top line and move */
  353.                                      /* following text up.       */
  354.  
  355.             b_gotorc(b, b_nrows(b)-1, 0); /* buffer=new last line */
  356.             for( i = b_ncols(b); --i >= 0 && *new_line ;)
  357.                 *b_advance(b) = *new_line++;
  358.             b_gotorc(b, b_nrows(b)-1, 0); /* Cursor to line start */
  359.             --brow;        /* make cursor track previous position */
  360.         }
  361.  
  362.         /* Scroll the viewport text up one line, then copy the
  363.          * associated text for the bottom line from the buffer
  364.          * to the viewport.
  365.          */
  366.  
  367.         if( !this->update )
  368.         {
  369.             if( vrow == 0 )  /* Old line is gone */
  370.             {
  371.                 /* col of new top line */
  372.                 b_gotorc(b, this->row_off, bcol);
  373.             }
  374.             else
  375.                 b_gotorc(b, brow,   bcol );
  376.         }
  377.         else
  378.         {
  379.             v_scroll(v, 1, 0);            /* scroll up one line  */
  380.             v_gotorc(v, v_nrows(v)-1, 0); /* last line, column 0 */
  381.  
  382.             /* copy buffer to screen */
  383.             for(i = v_ncols(v); --i >= 0 ;)
  384.                 v_putc(v, *b_advance(b), 1 );
  385.  
  386.             if( vrow == 0 )     /* Old line is gone     */
  387.             {                   /* can't restore cursor */
  388.                 b_gotorc(b, this->row_off, bcol);
  389.                 v_gotorc(v, 0, vcol);
  390.             }
  391.             else /* restore original cursor position */
  392.             {
  393.                 b_gotorc(b, brow,   bcol );
  394.                 v_gotorc(v, vrow-1, vcol );
  395.             }
  396.         }
  397.     }
  398.     else         /* Move text down, viewport moves up */
  399.     {                           /* relative to buffer */
  400.         if( this->row_off > 0 )
  401.         {
  402.             /* Viewport isn't banged up against top of buffer.
  403.              * Slide viewport over text.  Position buffer cursor
  404.              * at left edge of line with which to fill the
  405.              * viewport's opened-up top line. Note that b_gotorc()
  406.              * has side effects so the -- must be done separately.
  407.              */
  408.             --this->row_off;
  409.             b_gotorc(b, this->row_off, this->col_off );
  410.         }
  411.         else if( this->file_off <= 0 )
  412.         {
  413.             rv = 0;     /* may not scroll past initial top line */
  414.             goto exit;
  415.         }
  416.         else if( !import )              /* import is suppressed */
  417.         {
  418.             new_line = "";      /* pretend we got an empty line */
  419.             goto got_top;
  420.         }
  421.         else if(!this->import )   /* try to import new top line */
  422.         {
  423.             rv = 0;                       /* No import function */
  424.             goto exit;
  425.         }
  426.         else if( this->file_off <= 0 )
  427.         {
  428.             rv = 0;             /* Line doesn't exist */
  429.             goto exit;
  430.         }
  431.         else if(!(new_line=(*this->import)(this,this->file_off-1)))
  432.         {
  433.             rv = 0;             /* Couldn't import new line */
  434.             goto exit;
  435.         }
  436.         else    /* got the line, add it to the buffer & viewport */
  437.         {
  438. got_top:
  439.             --this->file_off;
  440.             b_gotorc  (b, b_nrows(b)-1, 0); /* export bottom line */
  441.             export    (this, b, this->file_off + b_nrows(b) );
  442.             b_closeup (b, ' ' );         /* delete bottom line */
  443.  
  444.             b_gotorc(b, 0, 0);           /* buffer = new top line */
  445.             for( i = b_ncols(b); --i >= 0 && *new_line ;)
  446.                 *b_advance(b) = *new_line++;
  447.             b_gotorc(b, 0, 0);     /* Cursor to start of new line */
  448.             ++brow;             /* cursor tracks previous position */
  449.         }
  450.  
  451.         /* Scroll the viewport text down one line, then copy the
  452.          * associated text for the top line from the buffer to the
  453.          * viewport.
  454.          */
  455.  
  456.         if( !this->update )
  457.         {
  458.             if(vrow >= v_nrows(v)-1)  /* Old line is gone */
  459.                 b_gotorc(b, this->row_off + v_nrows(v)-1, bcol);
  460.             else
  461.                 b_gotorc(b, brow, bcol );
  462.         }
  463.         else
  464.         {
  465.             v_scroll(v, -1, 0);             /* scroll down 1 line */
  466.             v_gotorc(v,  0, 0);             /* first line, col. 0 */
  467.             for(i = v_ncols(v); --i >= 0 ;) /* screen = buffer    */
  468.                 v_putc(v, *b_advance(b), 1 );
  469.  
  470.             if(vrow >= v_nrows(v)-1)  /* Old line is gone        */
  471.             {
  472.                 /* Can't restore old cursor position because the */
  473.                 /* line's not there anymore. go to the original  */
  474.                 /* column, but of the new bottom line            */
  475.  
  476.                 b_gotorc(b, this->row_off + v_nrows(v)-1, bcol);
  477.                 v_gotorc(v, v_nrows(v)-1, vcol);
  478.             }
  479.             else      /* Otherwise restore original cursor to be */
  480.             {         /* under original character                */
  481.                 b_gotorc(b, brow,   bcol );
  482.                 v_gotorc(v, vrow+1, vcol );
  483.             }
  484.         }
  485.     }
  486. exit:
  487.     monitor(this, UPDATE);
  488.     return rv;
  489. }
  490. /*------------------------------------------------------------*/
  491. int draw( win *const this, int ask_top, int ask_left )
  492. {
  493.     /* Fill the viewport with text from the buffer, the viewport
  494.      * is at offset "top" from the top line of the buffer and
  495.      * "left" from the left edge of the buffer. Cursor doesn't
  496.      * move with respect to the viewport. Top and left are
  497.      * truncated if necessary to prevent the viewport from
  498.      * moving off the edge of the buffer. Return false if the
  499.      * coordinates were modified to draw things, true otherwise.
  500.      */
  501.  
  502.     viewport *const v = &this->v;
  503.     textbuf  *const b = &this->b;
  504.     int col        = 0;
  505.     int row        = 0;
  506.     int nrows      = v_nrows( v );
  507.     int ncols      = v_ncols( v );
  508.     const int left = max(0, min(ask_left, b_ncols(b)-v_ncols(v)));
  509.     const int top  = max(0, min(ask_top,  b_nrows(b)-v_nrows(v)));
  510.     int cur_top    = top;
  511.     int start_row  = v_row(v);
  512.     int start_col  = v_col(v);
  513.  
  514.     if( !this->update ) /* no updating, pretend we succeeded */
  515.         return 1;
  516.  
  517.     while( --nrows >= 0 )
  518.     {
  519.         /* both b_gotorc and v_gotorc have side effects */
  520.  
  521.         b_gotorc( b, cur_top, left ); ++cur_top;
  522.         v_gotorc( v, row,     0    ); ++row;
  523.  
  524.         for( col = ncols; --col> 0 ;)     /* all but last char. */
  525.             v_putc(v, *b_advance(b), 1 );
  526.         v_putc(v, *b_current(b), 0 );     /* last character.    */
  527.     }
  528.  
  529.     this->row_off = top;
  530.     this->col_off = left;
  531.     v_gotorc(v, start_row,       start_col        );
  532.     b_gotorc(b, start_row + top, start_col + left );
  533.  
  534.     monitor(this, UPDATE);
  535.     return( top == ask_top && left == ask_left );
  536. }
  537. /*------------------------------------------------------------*/
  538. static int hscroll( win *const this, int left )
  539. {
  540.     /* Scroll horizontally, "left" columns to the left (right if
  541.      * negative).  There's no import/export feature here, so a
  542.      * scroll past the edge of the buffer is illegal. Return true
  543.      * if you moved the required number of columns, false if not.
  544.      * Note that there still might have been motion in the earlier
  545.      * case, just not enough of it. A request of zero columns
  546.      * to the left just does a redraw.
  547.      */
  548.  
  549.     return draw( this, this->row_off, this->col_off + left);
  550. }
  551. /*------------------------------------------------------------*/
  552. int e_update( win *const this, int enable )
  553. {
  554.     /* If "enable" is false, store a snapshot of the current
  555.      * viewport and file states. If it's true, attempt to restore
  556.      * previous state (cursor position and relative positions
  557.      * of the file, buffer, and viewport).
  558.      *
  559.      * This routine is meant primarily to disable screen updating
  560.      * while the file is being examined. Return 1 if you change
  561.      * state 1 if the current state is requested.
  562.      */
  563.  
  564.     if( this->update == enable )        /* nothing to do */
  565.         return 0;
  566.  
  567.     if( !enable )               /* disabling */
  568.     {
  569.         this->update = 0;
  570.         this->update_row_off    = this->row_off;
  571.         this->update_col_off    = this->col_off;
  572.         this->update_file_off   = this->file_off;
  573.         this->update_row        = e_row(this);
  574.         this->update_col        = e_col(this);
  575.         this->dirty             = 0;
  576.     }
  577.     else                                /* enabling */
  578.     {
  579.         /* Get the cursor back to the original place with
  580.          * an e_gotorc(), then scroll the screen as necessary
  581.          * to get the cursor positioned correctly relative to
  582.          * the viewport.
  583.          */
  584.  
  585.         e_gotorc( this, this->update_row, this->update_col );
  586.  
  587.         while( this->file_off < this->update_file_off )
  588.             if( !e_vscroll(this, 1, 1) )
  589.                 break;
  590.         while( this->file_off > this->update_file_off )
  591.             if( !e_vscroll(this, 0, 1) )
  592.                 break;
  593.  
  594.         while( this->row_off < this->update_row_off )
  595.             if( !e_vscroll(this, 1, 1) )
  596.                 break;
  597.         while( this->row_off > this->update_row_off )
  598.             if( !e_vscroll(this, 0, 1) )
  599.                 break;
  600.  
  601.         this->update = 1;
  602.         if( this->col_off != this->update_col_off )
  603.             fprintf(stderr,"INTERNAL WINDOW ERROR: "
  604.                            "unexpected horiz motion\n");
  605.  
  606.         if( this->dirty )
  607.             draw( this, this->row_off, this->col_off );
  608.     }
  609.     monitor(this, UPDATE);
  610.     return 1;
  611. }
  612. /*------------------------------------------------------------*/
  613. int e_gotorc( win *const this, int dst_r, int dst_c )
  614. {
  615.     /* Go to the indicated virtual row and column. If r and c
  616.      * are inside the viewport, just go there. If not, scroll
  617.      * the viewport as appropriate. New lines are imported if
  618.      * you request a row address that is not in the buffer.
  619.      * Note that this subroutine might be used to add lines to
  620.      * the end of the file (by going down from the last line).
  621.      */
  622.  
  623.     viewport *const v = &this->v;
  624.     textbuf  *const b = &this->b;
  625.     int   rv = 1;                       /* return value */
  626.     int   r, c;
  627.     int   nrows = v_nrows( v );
  628.     int   ncols = v_ncols( v );
  629.  
  630.     switch( dst_r )
  631.     {
  632.     case E_HOME: dst_r = this->row_off;           break;
  633.     case E_MID:  dst_r = this->row_off + nrows/2; break;
  634.     case E_LAST: dst_r = this->row_off + nrows-1; break;
  635.     }
  636.     r = dst_r;
  637.  
  638.     if( !(0 <= dst_c && dst_c < b_ncols(b)) || r < 0 )
  639.     {
  640.         rv = 0;         // Illegal address
  641.         goto exit;
  642.     }
  643.     c = dst_c;
  644.  
  645.     /* Get the desired column into the viewport, starting with
  646.      * column index:
  647.      *     Modify c to hold the distance that c is from the right
  648.      *     of the viewport. For example, c will be 0 if it's
  649.      *     the first column past the one in the right edge of the
  650.      *     viewport. If c is -1, it's at the right edge of the
  651.      *     viewport. If it's -ncols then it's at the left edge of
  652.      *     the viewport. Then, scroll as necessary to get c into
  653.      *     the viewport.
  654.      */
  655.  
  656.     c -= this->col_off + ncols;
  657.     if( c >= 0 )
  658.     {
  659.         while( c-- >= 0 )
  660.             if( !hscroll(this, 1) )     /* scroll left */
  661.             {
  662.                 rv = 0;
  663.                 goto exit;
  664.             }
  665.     }
  666.     else if( c < -ncols )
  667.     {
  668.         c += ncols;     /* c = -1 ==> 1 col to left of viewport. */
  669.         while( ++c <= 0 )
  670.             if( !hscroll(this, -1) )    /* scroll right */
  671.             {
  672.                 rv = 0;
  673.                 goto exit;
  674.             }
  675.     }
  676.  
  677.     /* Do the same thing with the vertical coordinate. The
  678.      * situation is more complicated here, though, because the row
  679.      * coordinate is file relative. In other words:
  680.      *
  681.      *  r == file_off + row_off + v_row(v)
  682.      *
  683.      * After the subtract, below, r will hold the distance between
  684.      * the top of the viewport and the desired row, negative if
  685.      * the desired row is above the viewport.
  686.      */
  687.  
  688.     r -= this->row_off + this->file_off;
  689.     if( r < 0 )         /* desired row is above viewport */
  690.     {
  691.         while( ++r <= 0 )
  692.             if( !e_vscroll(this, -1, 1) )  /* scroll text down */
  693.             {
  694.                 rv = 0;
  695.                 goto exit;
  696.             }
  697.     }
  698.     else if( r >= nrows )
  699.         for( r -= (nrows-1); --r >= 0 ;)
  700.             if( !e_vscroll(this, 1,1) ) /* scroll text up */
  701.             {
  702.                 rv = 0;
  703.                 goto exit;
  704.             }
  705.  
  706.     /* the desired cursor is now in the viewport. Go there. */
  707.  
  708.     b_gotorc(b, dst_r - this->file_off, dst_c);
  709.     if( this->update )
  710.         v_gotorc(v, dst_r - this->row_off - this->file_off,
  711.                                     dst_c - this->col_off );
  712. exit:
  713.     monitor(this, UPDATE);
  714.     return rv;
  715. }
  716. /*------------------------------------------------------------*/
  717. static int bottom(win *const this, int offset )
  718. {
  719.     /* if offset isn't zero, set bottom to it's current
  720.      * value + offset, otherwise set it to the current line.
  721.      */
  722.  
  723.     if( !offset )
  724.     {
  725.         int lineno = b_row(&this->b) + this->file_off;
  726.         if( lineno > this->bottom )
  727.             this->bottom = lineno;
  728.     }
  729.     else if( (this->bottom += offset) < -1 )
  730.         this->bottom = -1;
  731.  
  732.     return this->bottom;
  733. }
  734. /*------------------------------------------------------------*/
  735. void e_replace (win *const this,int c)
  736. {
  737.     bottom(this,0);
  738.     if( c )
  739.     {
  740.         this->dirty = 1;
  741.         if( this->update )
  742.             v_putc( &this->v, c, 0 );
  743.         *b_current(&this->b) = c;
  744.     }
  745.     monitor(this, UPDATE);
  746. }
  747. /*------------------------------------------------------------*/
  748. void e_ins(win *const this, int c )
  749. {
  750.     viewport *const v = &this->v;
  751.  
  752.     bottom(this,0);
  753.     b_insert_c( &this->b, 1, c ? c : ' ');
  754.     this->dirty = 1;
  755.     if( this->update )
  756.     {
  757.         v_scroll_region( v, 0, -1, v_col(v),     v_row(v),
  758.                                    v_ncols(v)-1, v_row(v) );
  759.         v_putc( v, c ? c : ' ', 0 );
  760.     }
  761.     monitor(this, UPDATE);
  762. }
  763. /*------------------------------------------------------------*/
  764. void e_del(win *const this)
  765. {
  766.     viewport *const v = &this->v;
  767.     textbuf  *const b = &this->b;
  768.     int vrow = v_row(v);
  769.     int vcol = v_col(v);
  770.     int brow = b_row(b);
  771.     int bcol = b_col(b);
  772.  
  773.     b_delete_c( &this->b, 1, ' ' );
  774.     this->dirty = 1;
  775.  
  776.     if( this->update )
  777.     {
  778.         v_scroll_region( v, 0,  1,      v_col(v), v_row(v),
  779.                                     v_ncols(v)-1, v_row(v) );
  780.         v_gotorc (v, vrow, v_ncols(v)-1);
  781.     }
  782.     b_gotorc (b, brow, (v_ncols(v)-1) + this->col_off);
  783.  
  784.     if( this->update )
  785.     {
  786.         v_putc   (v, *b_current(b), 0);
  787.         v_gotorc (v, vrow, vcol);
  788.     }
  789.     b_gotorc(b, brow, bcol);
  790.     monitor(this, UPDATE);
  791. }
  792. /*------------------------------------------------------------*/
  793. int e_closeline(win *const this)
  794. {
  795.     viewport *const v = &this->v;
  796.     textbuf  *const b = &this->b;
  797.     int    ecol = e_col(this);  /* absolute row and column */
  798.     int    erow = e_row(this);
  799.     char   *new;
  800.     int    i, bottom_line_of_buffer;
  801.  
  802.     if( this->bottom < 0 )
  803.         return 0;
  804.  
  805.     b_gotorc ( b, b_row(b), 0  );
  806.     del_line ( this, b, this->file_off + b_row(b) );
  807.     this->dirty = 1;
  808.  
  809.     b_closedown( b, ' ');
  810.     if( this->update )
  811.         v_scroll_region( v, 1, 0, 0, v_row(v), v_ncols(v)-1,
  812.                                                 v_nrows(v)-1 );
  813.  
  814.     bottom(this, -1);   /* Bottom line moves up one notch
  815.                          * because of the delete.
  816.                          */
  817.     /* Import a new bottom line into the buffer if there's an
  818.      * import function and there is text in the file below the
  819.      * bottom line of the buffer.
  820.      *
  821.      * Note that since we just deleted a buffer line, all of the
  822.      * file-relative line numbers have changed, so the line being
  823.      * imported is actually the last line in the buffer, not the
  824.      * line below the last line as was the case in the vertical-
  825.      * scroll function.
  826.      */
  827.  
  828.     bottom_line_of_buffer = this->file_off + b_nrows(b) - 1;
  829.  
  830.     if( this->import && this->bottom > bottom_line_of_buffer )
  831.     {
  832.         if(new = (*this->import)(this, bottom_line_of_buffer) )
  833.         {
  834.             /* Move the imported line into the buffer */
  835.  
  836.             b_gotorc(b, b_nrows(b)-1, 0 );
  837.             for( i = b_ncols(b); --i >= 0 && *new ;)
  838.                 *b_advance(b) = *new++;
  839.         }
  840.     }
  841.  
  842.     /* If original row is now off the bottom of the file,
  843.      * adjust it down to compensate.
  844.      */
  845.  
  846.     if( erow > this->bottom )
  847.         erow = this->bottom < 0 ? 0 : this->bottom ;
  848.  
  849.     /* Update the viewport to display the imported line
  850.     */
  851.  
  852.     if( this->update )
  853.     {
  854.         b_gotorc(b, v_nrows(v)-1 + this->row_off, this->col_off );
  855.         v_gotorc(v, v_nrows(v)-1, 0 );
  856.         for( i = v_ncols(v); --i >= 0; v_putc(v,*b_advance(b),1) )
  857.             ;
  858.     }
  859.  
  860.     e_gotorc(this, erow, ecol );
  861.     e_gotorc(this, e_row(this), e_first(this) );
  862.     monitor(this, UPDATE);
  863.     return 1;
  864. }
  865. /*------------------------------------------------------------*/
  866. int e_openline(win *const this, int below)
  867. {
  868.     /* open the line on which the cursor is resting by moving all
  869.      * following lines down a notch and importing a blank line.
  870.      * If below is true, open the line beneath the current one.
  871.      * Cursor ends up at far left of new line.
  872.      */
  873.  
  874.     textbuf  *b  = &this->b;
  875.     viewport *v  = &this->v;
  876.     int line     = e_row(this);
  877.     int scrolled = 0;
  878.     int row;
  879.  
  880.     this->dirty = 1;
  881.     if( below )
  882.     {
  883.         /* If we're inserting below the last line of the file, and
  884.          * the cursor's resting on the bottom line of both the
  885.          * viewport and the buffer, force a scroll up without
  886.          * importing. In any event, adjust the target line to be
  887.          * the one beneath the current on.
  888.          */
  889.  
  890.         if( (line == this->bottom) && v_ateob(v) && b_ateob(b) )
  891.             scrolled = e_vscroll( this, 1, 0 );
  892.         ++line;
  893.     }
  894.     e_gotorc(this, line, 0); /* go to line that will be replaced */
  895.  
  896.     /* export the bottom line of the buffer if it has something
  897.      * in it
  898.      */
  899.  
  900.     if( this->bottom >= b_nrows(b)-1 && !scrolled )
  901.     {
  902.         row = b_row(b);
  903.         b_gotorc  (b, b_nrows(b)-1, 0);
  904.         export    (this, b, this->file_off + b_row(b) );
  905.         b_gotorc  (b, row, 0);
  906.     }
  907.  
  908.     /* Scroll everything below current line down a notch and
  909.      * put cursor at start of new line.
  910.      */
  911.  
  912.     b_opendown(b,' ');
  913.     if( this->update )
  914.         v_scroll_region(v, -1, 0, 0, v_row(v), v_ncols(v)-1,
  915.                                                v_nrows(v)-1 );
  916.     e_gotorc(this, line, 0);
  917.  
  918.     /* Update file size and tell file-manager to insert line at
  919.      * current position
  920.      */
  921.  
  922.     bottom(this, +1);   /* update the file size */
  923.     return (this->insert) ? (*this->insert)( this, line ) : 1 ;
  924. }
  925. /*------------------------------------------------------------*/
  926. void e_cleartoeol( win *const this )
  927. {
  928.     this->dirty = 1;
  929.     b_cleartoeol( &this->b,' ');
  930.     if( this->update )
  931.         v_cleartoeol( &this->v );
  932.     monitor(this, UPDATE);
  933. }
  934.